package simplifiedPastry;

import java.math.BigInteger;

import simplifiedPastry.Node.NodeEntry;

class RoutingTable {
	
	private NodeEntry[][]	routingTable;
	private int rtSize;
	private NodeId nodeId;
	private NodeEntry nodeEntry;
	
	public RoutingTable(NodeEntry nodeEntry) {
		routingTable		= new NodeEntry[32][16];
		rtSize				= 0;
		this.nodeEntry		= nodeEntry;
		this.nodeId			= nodeEntry.nodeId;
	}
	
	synchronized public void fillRoutingTableBuff(byte[] buff, int offset) {
		if (rtSize == 0) {
			return;
		}
		NodeEntry[] rtArray = new NodeEntry[rtSize];
		int rtIndex = 0;
		for(int i = 0; i < 32; ++i) {
			for (int j = 0; j < 16; ++j) {
				if(routingTable[i][j] != null) {
					rtArray[rtIndex++] = routingTable[i][j];
				}
				
			}
		}
		for(int i = 0; i < rtSize; ++i) {
			rtArray[i].fillByteBuff(buff, offset);
			offset += 22;
		}
	}
	
	synchronized public NodeEntry getNextNodeEntry(NodeId searchId) {
		if (searchId.compareTo(nodeId) == 0) {
			System.err.println("getNextNodeEntry(NodeId searchId) --should never happen!");
		}
		int minPrefix = nodeId.sameGroupNum(searchId), maxPrefix = 0;
		NodeEntry closestEntry = nodeEntry, maxPrefixEntry = null;
		BigInteger closestDistance = nodeEntry.nodeId.distance(searchId), dist;
		for(int i = 31; i >= 0; --i) {
			for(int j = 15; j >= 0; --j) {
				if (routingTable[i][j] != null) {
					int prefixSize = routingTable[i][j].nodeId.sameGroupNum(searchId);
					if (prefixSize > minPrefix && prefixSize > maxPrefix) {
						maxPrefix = prefixSize;
						maxPrefixEntry = routingTable[i][j];
					} else if (prefixSize == minPrefix) {
						dist = routingTable[i][j].nodeId.distance(nodeId); 
						if (dist.compareTo(closestDistance) < 0) {
							closestDistance = dist;
							closestEntry = routingTable[i][j];
						}
					}
				}
			}
		}
		if (maxPrefix > minPrefix) {
			return maxPrefixEntry;
		}
		if (closestEntry != nodeEntry) {
			return closestEntry;
		}
		return null;
	}
	
	synchronized public int getSize() {
		return rtSize;
	}
	
	synchronized public void addNodeEntry(NodeEntry nodeEntry) {
		if (nodeEntry.nodeId.compareTo(nodeId) == 0) {
			return ;
		}
		int row = nodeId.routingTableRow(nodeEntry.nodeId);
		int col = nodeId.routingTableColumn(nodeEntry.nodeId, row);
		if (routingTable[row][col] == null) {
			++rtSize;
		}
		routingTable[row][col] = nodeEntry;
	}
	
	synchronized public void removeNodeEntry(NodeEntry nodeEntry) {
		if (nodeEntry.nodeId.compareTo(nodeId) == 0) {
			System.err.println("removeNodeEntry(NodeEntry nodeEntry) --should never happen!");
		}
		int row = nodeId.routingTableRow(nodeEntry.nodeId);
		int col = nodeId.routingTableColumn(nodeEntry.nodeId, row);
		if (routingTable[row][col].nodeId.compareTo(nodeEntry.nodeId) == 0) {
			if (routingTable[row][col] != null) {
				--rtSize;
			}
			routingTable[row][col] = null;
		}
	}
	
	synchronized public NodeEntry[] getRTEntries() {
		if (rtSize == 0) {
			return new NodeEntry[0];
		}
		NodeEntry[] rtArray = new NodeEntry[rtSize];
		int rtIndex = 0;
		for(int i = 0; i < 32; ++i) {
			for (int j = 0; j < 16; ++j) {
				if(routingTable[i][j] != null) {
					rtArray[rtIndex++] = routingTable[i][j];
				}
				
			}
		}
		return rtArray;
	}
}
